home *** CD-ROM | disk | FTP | other *** search
- #!/usr/bin/perl
- #
- # stacknews - create and maintain a folder stack for reading news
- #
- # This program builds a folder stack (separate from the MH folder stack)
- # of news groups to be read. It prints the path to the appropriate
- # MH context file. A separate MH context is kept for each news group.
- # This should speed up certain MH operations and avoids the problem of
- # having news group folders in your main MH context.
- #
- # Usage:
- # setenv MHCONTEXT \
- # `stacknews [-(no)check] [-(no)clear] [-(no)debug] [-(no)list]
- # [-(no)next] [-(no)push] [-(no)rigor] group ...`
- #
- # Options:
- # -check find the first group with new articles
- # -clear clear the folder stack
- # -debug turn on debugging
- # -list list the folder stack
- # -next pop the folder stack
- # -nocheck push all groups unconditionally
- # -noclear do not clear the folder stack
- # -nodebug operate in normal (non-debug) mode
- # -nolist do not list the folder stack
- # -nonext do not pop the folder stack
- # -nopush do not push groups onto the folder stack
- # -norigor use defaults or the environment ONLY
- # -push push groups onto the folder stack
- # -rigor check the MH profile (the usual MH stuff)
- #
- # Defaults: -check -noclear -nodebug -nonext -push -rigor
- #
- # Any groups on the command line are pushed onto the stack.
- # If no groups are specified, then all default groups are
- # pushed onto the stack.
- #
- # Mutually exclusive options: -clear, -list, -next, -push
- #
- # Some useful code to include in your .cshrc file:
- #
- # if (! $?stacknews) then
- # set stacknews = "stacknews"
- # endif
- # alias sninvo 'setenv MHCONTEXT `$stacknews \!*`'
- # alias news 'set stacknews = "stacknews" ; sninvo'
- # alias fnews 'set stacknews = "stacknews.f" ; sninvo'
- # alias g 'setenv MHCONTEXT `$stacknews -next \!*`'
- # alias q 'folder -fast last > /dev/null ; g'
- # alias u 'setenv MHCONTEXT `$stacknews -nopush \!*`'
- # alias z '$stacknews -clear \!*; unsetenv MHCONTEXT'
- # alias nglist '$stacknews -list \!*'
- #
- # The code above defines the following commands:
- # g -- Go to the next news group.
- # news -- Read new news groups.
- # nglist -- See the current state of the news stack.
- # q -- Set the last article to be the current article
- # -- and go to the next news group.
- # sninvo -- Utility alias to invoke stacknews.
- # u -- Set ("update") another shell's MHCONTEXT ...
- # but unfortunately doesn't handle the case
- # of an alternative set of news groups, e.g.
- # as read by stacknews.f
- # z -- Stop reading news altogether.
- #
- #
- # Environment variables:
- # HOME path of home directory
- # MH path of MH profile file
- # MHCONTEXT path of current MH context file
- # NEWSARTS path of top directory containing news articles
- # NEWSCONTEXTS path of directory containing folder contexts
- # NEWSGROUPS list of colon-separated news groups to read
- #
- # MH profile components:
- # context path of MH context file
- # path path of MH directory
- # (prog) default options
- # (prog)-arts path of top directory containing news articles
- # (prog)-contexts path of directory containing folder contexts
- # (prog)-defaults path of file containing list of news groups
- # (prog)-groups list of comma-separated news groups to read
- #
- # Note: "(prog)" above is the invocation name of the program;
- # MH profile components might be named stacknews-defaults,
- # stacknews-groups, and stacknews-arts, if the program is invoked
- # as "stacknews".
- #
- # Files and Directories:
- # $HOME/.mh_profile default MH profile
- # $HOME/.news-contexts default news folder contexts directory
- # $HOME/.news-contexts/Stack news folder stack
- # $HOME/.news-defaults list of default news groups
- # $HOME/Mail default MH directory
- # $HOME/Mail/context default MH context file
- #
- # Author: Jerry Sweet <jns@fernwood.mpk.ca.us>
- # Taken from my old, similar, but much slower, "news" program.
- #
- # $Header: /q2/uh/jsweet/src/mh-front/RCS/stacknews,v 1.6 1992/06/11 18:24:52 jsweet Exp $
-
- #
- # Subprograms
- #
-
- sub relative_to_absolute {
- local($_) = @_;
-
- if ($_ !~ /^\//) {
- $_ = sprintf("%s%s%s", $mhroot, $_ ? '/' : '', $_);
- }
- if ($_ !~ /^\//) {
- $_ = sprintf("%s/%s", $ENV{'HOME'}, $_);
- }
- $_;
- }
-
- sub destroy_cur {
- local($dir) = @_;
- local($sequence_file) = "$dir/.mh_sequences";
-
- unlink $sequence_file if ($update_cur && -e $sequence_file);
- # This is the only known way (in MH 6.6 at least) to reset cur if new
- # messages have arrived in a folder (via rcvstore) after all messages
- # were removed or refiled.
- }
-
- #
- # Main
- #
-
- umask 077; # This should be adjustable, but isn't at the moment.
-
- ($prog = $0) =~ s%.*/%%;
-
- $newsarts = defined $ENV{'NEWSARTS'} ? $ENV{'NEWSARTS'} : '/usr/spool/news';
- $HOME = $ENV{'HOME'} ;
- $profile = defined $ENV{'MH'} ? $ENV{'MH'} : "$HOME/.mh_profile";
- $mhroot = "$HOME/Mail";
- if (defined $ENV{'NEWSGROUPS'}) {
- @groups = split(/:/, $ENV{'NEWSGROUPS'});
- }
- $contexts = defined $ENV{'NEWSCONTEXTS'} ?
- $ENV{'NEWSCONTEXTS'} :
- "$HOME/.news-contexts";
- $mhcontext = defined $ENV{'MHCONTEXT'} ?
- $ENV{'MHCONTEXT'} :
- "$mhroot/context";
-
- $check_folders = 1;
- $rigor = 1;
- $push = 1;
-
- # Look for the -norigor option in the command line.
-
- while ($_ = shift @ARGV) {
- push(@Args, $_);
- /^-norigor$/ && $rigor--;
- /^-rigor$/ && $rigor++;
- }
-
- if ($rigor > 0) {
- $ndfile = -e "$HOME/.news-defaults" ? "$HOME/.news-defaults" : '';
-
- $profile_component = 'x-bogus';
-
- if (! open(PROFILE, "<$profile")) {
- print STDERR "$prog: can't open profile \"$profile\" - $!\n";
- }
- else {
- while (<PROFILE>) {
- /^(\S+):\s*/ && do {
- ($profile_component = $1) =~ tr/A-Z/a-z/;
- $PROFILE_COMPONENT{$profile_component} = $';
- };
-
- /^[ \t]/ && do {
- $PROFILE_COMPONENT{$profile_component} .= $_;
- };
- }
-
- if (defined $PROFILE_COMPONENT{'path'}) {
- $_ = $PROFILE_COMPONENT{'path'};
- /^\s*(\S+)/ && do { $mhroot = $1; };
- }
- if (defined $PROFILE_COMPONENT{'context'}) {
- $_ = $PROFILE_COMPONENT{'context'};
- /^\s*(\S+)/ && do {
- $mhcontext = $1;
- if ($mhcontext !~ /^\//) {
- $mhcontext = "$mhroot/$mhcontext";
- }
- };
- }
- if (defined $PROFILE_COMPONENT{$prog}) {
- $_ = $PROFILE_COMPONENT{$prog};
- @Args = (split, @Args);
- }
- if (defined $PROFILE_COMPONENT{"$prog-arts"}) {
- $_ = $PROFILE_COMPONENT{"$prog-arts"};
- /^\s*(\S+)/ && do { $newsarts = $1; };
- }
- if (defined $PROFILE_COMPONENT{"$prog-contexts"}) {
- $_ = $PROFILE_COMPONENT{"$prog-contexts"};
- /^\s*(\S+)/ && do {
- $contexts = $1;
- if ($contexts !~ /^\//) {
- $contexts = "$mhroot/$contexts";
- }
- };
- }
- if (defined $PROFILE_COMPONENT{"$prog-defaults"}) {
- $_ = $PROFILE_COMPONENT{"$prog-defaults"};
- /^\s*(\S+)/ && do {
- $ndfile = $1;
- if ($contexts !~ /^\//) {
- $contexts = "$mhroot/$contexts";
- }
- };
- }
- if (defined $PROFILE_COMPONENT{"$prog-groups"}) {
- $_ = $PROFILE_COMPONENT{"$prog-groups"};
- @profile_groups = split(/\s*,\s*/);
- }
- }
- }
-
- # Evaluate the switches:
-
- while ($_ = shift @Args) {
- /^-check$/ && $check_folders++;
- /^-clear$/ && $clear_stack++;
- /^-debug$/ && $debug++;
- /^-list$/ && $list++;
- /^-next$/ && $next++;
- /^-nocheck$/ && $check_folders--;
- /^-noclear$/ && $clear_stack--;
- /^-nodebug$/ && $debug--;
- /^-nolist$/ && $list--;
- /^-nonext$/ && $next--;
- /^-nopush$/ && $push--;
- /^-push$/ && $push++;
- /^[^-]/ && push(@switch_groups, $_);
- }
-
- if (! -d $contexts) {
- if (! mkdir($contexts, 0700)) {
- print STDERR "$prog: can't mkdir \"$contexts\" - $!\n";
- exit 1;
- }
- }
-
-
- $folder_stack = "$contexts/Stack";
-
- if ($clear_stack > 0) {
- @groups = ();
- }
- else {
- # Read the folder stack:
-
- if (-e $folder_stack) {
- if (! open(STACK, "<$folder_stack")) {
- print STDERR
- "$prog: can't open folder stack file \"$folder_stack\" - $!\n";
- print "$mhcontext\n" unless ! $print_context;
- exit 1;
- }
- foreach (<STACK>) {
- next if /^#|^\s*$/;
- push(@stack, split);
- }
- }
-
- if ($list > 0) {
- foreach (@stack) {
- push(@group_list, $_);
- }
-
- if ($#group_list >= 0) {
- print STDERR join(' ', @group_list), "\n";
- }
- exit 0;
- }
- }
-
- shift(@stack) if ($next > 0); # If we saw -next, then pop the stack.
-
- if ($clear_stack <= 0 && $next <= 0 && $push > 0) {
- # Decide on the news groups at which to look:
-
- if ($#switch_groups < 0 && $#profile_groups >= 0) {
- push(@groups, @profile_groups);
- # Select groups from the (prog)-groups: component.
- }
-
- if ($#groups < 0 && $#switch_groups < 0 && $ndfile) {
- # Look at the .news-defaults file:
-
- if (! open(NDFILE, $ndfile)) {
- print STDERR "$prog: can't open news defaults file \"$ndfile\" - $!\n";
- print "$mhcontext\n" unless ! $print_context;
- exit 1;
- }
-
- while (<NDFILE>) {
- next if /^#|^\s*$/;
- split;
- push(@groups, @_);
- }
- }
-
- # Push news groups onto the folder stack:
-
- push(@stack, @groups);
-
- # Push groups from the command line onto the front of the stack:
-
- foreach (reverse(@switch_groups)) {
- unshift(@stack, $_);
- }
- }
-
- if ($clear_stack <= 0 && $push > 0) {
- # Find the first group for which new articles have arrived:
-
- if ($check_folders > 0) {
- select(STDERR);
- $| = 1; # Force output of '.' as we look for a good group
- select(STDOUT);
- }
-
- $newsarts_cur = $newsarts;
-
- while ($#stack >= 0) {
- ($_ = $stack[0]) =~ s/\s+//g;
-
- # An item beginning with '+' specifies a folder instead of
- # a news group.
-
- if (/^\+/) {
- $_ = $';
- $group = $_;
- $dir = &relative_to_absolute($_);
- $folder = $_;
- s%/%.%g;
- $cur_context = "$contexts/FOLDER.$_";
- # Bug: +foo.bar will have the same context as +foo/bar.
- $update_cur = 1;
- }
- else {
- $group = $_;
- s%\.%/%g;
- $dir = "$newsarts_cur/$_";
- $folder = $dir;
- $cur_context = "$contexts/$group";
- $update_cur = 0;
- }
-
- # print STDERR "group = \"$group\"\n",
- # "dir = \"$dir\"\n",
- # "folder = \"$folder\"\n",
- # "ctxt = \"$cur_context\"\n",
- # "stack = ", join(' ', @stack), "\n"
- # ;
-
- last if ($check_folders <= 0);
-
- if (! -d $dir) {
- print STDERR "\nno such news group as $group\n";
- shift @stack;
- next;
- }
-
- $ENV{'MHCONTEXT'} = $cur_context;
- $finfo = `folder +$folder`;
- if ($? >> 8) {
- print STDERR "\nfolder change to +$folder failed\n";
- shift @stack;
- next;
- }
- print(STDERR $finfo) if ($debug > 0);
-
- if ($finfo =~ /\s+has\s+no\s+/) {
- # There are no messages in this folder, so ensure that the cur
- # sequence is destroyed and go on to the next folder.
-
- &destroy_cur($dir);
- print STDERR '.';
- $dots++;
- shift @stack;
- next;
- }
-
- if ($finfo =~ /cur=\s*(\d+)/) {
- $cur = $1;
- }
- else {
- # There are messages in here, but there's no cur message apparent,
- # so reset the cur sequence.
-
- &destroy_cur($dir);
- $cur = 0;
- }
-
- if ($finfo =~ /message.*\(\s*(\d+)-\s*(\d+)\s*\)/) {
- $lastm = $2;
-
- if ($cur >= $lastm) {
- # We don't have any new messages in this folder, so go on to
- # the next one. (Bug: we should check for an "unseen" sequence.)
-
- print STDERR '.';
- $dots++;
- shift @stack;
- next;
- }
- else {
- last; # Found a live one.
- }
- }
- } # end while
-
- if ($dots > 0) {
- print STDERR "\n";
- select(STDERR);
- $| = 0;
- select(STDOUT);
- }
- }
-
- # Rewrite the folder stack file:
-
- if ($clear_stack > 0) {
- @stack = ();
- }
-
- if ($push > 0) {
- if (! open(STACK, ">$folder_stack")) {
- print STDERR
- "$prog: can't rewrite folder stack file \"$folder_stack\" - $!\n";
- print "$mhcontext\n" unless ! $print_context;
- exit 1;
- }
- if ( (! print STACK join(' ', @stack)) || ! close(STACK) ) {
- print STDERR
- "$prog: can't write folder stack file \"$folder_stack\" - $!\n";
- print "$mhcontext\n" unless ! $print_context;
- exit 1;
- }
- }
-
- if ($clear_stack > 0) {
- exit 0;
- }
-
- # Tell the user what the state of the folder stack is:
-
- if ($#stack >= 0) {
- $_ = $stack[0];
-
- if ($next <= 0) {
- printf(STDERR "%d news group%s selected; topmost: %s\n",
- $#stack + 1,
- $#stack > 0 ? 's' : '',
- $_);
- }
- else {
- print STDERR "topmost: $_\n";
- }
-
- if (/^\+/) {
- $_ = $';
- s%/%.%g;
- print "$contexts/FOLDER.$_\n";
- }
- else {
- print "$contexts/$_\n";
- }
- }
- else {
- if ($next <= 0) {
- print STDERR "no news groups selected\n";
- }
- print "$mhcontext\n" unless ! $print_context;
- }
-
- exit 0;
-